/**
 * Implement the hierarchy mesh. 
 * @author Songgang Xu (sxu at tamu dot edu)
 * @file mesh.h
 */


#ifndef XMESH_HIERARCHY_MESH_H
#define XMESH_HIERARCHY_MESH_H

#include "mesh.h"
#include "aabbtree.h"
namespace math 
{
	template<typename mesh_traits>
		class HierarchyMesh : public Mesh <mesh_traits>
	{
	public:
	   	typedef typename mesh_traits::ValueType              ValueType; 
		typedef typename mesh_traits::FaceType               FaceType;
		typedef typename mesh_traits::VertexType             VertexType;
		typedef typename mesh_traits::HalfEdgeType           HalfEdgeType ; 
		typedef typename mesh_traits::EdgeType               EdgeType ; 
		typedef typename mesh_traits::VectorType             VectorType ;
		typedef typename mesh_traits::PointType              PointType ;
		//typedef typename mesh_traits::MeshType               MeshType ;

		typedef typename Mesh<mesh_traits>::HalfEdgeIterator HalfEdgeIterator ;
		typedef typename Mesh<mesh_traits>::VertexIterator   VertexIterator ;
		typedef Segment<ValueType, mesh_traits::dim>         SegmentType ;
		typedef Ray<ValueType, mesh_traits::dim>             RayType ;

		typedef AABBTreePoly <ValueType, mesh_traits::dim>          AABBTreePolyType; 
		typedef AABBTreePolyBuilder <ValueType, mesh_traits::dim>   AABBTreePolyBuilderType  ; 
		typedef AABBTreePolygon <ValueType, mesh_traits::dim>       AABBTreePolygonType ;
		typedef AABBTreeNode <ValueType, mesh_traits::dim>          AABBTreeNodeType ;

		typedef Box<ValueType, mesh_traits::dim>                    BoxType; 
		typedef Polygon<ValueType, mesh_traits::dim>                PolygonType; 
		typedef Sphere<ValueType, mesh_traits::dim>                 SphereType; 

		typedef typename AABBTreePolyType::PolygonColResult         PolygonColResult;
		typedef typename AABBTreePolyType::RayColResult             RayColResult;
		typedef typename AABBTreePolyType::SphereColResult          SphereColResult;
		typedef typename AABBTreePolyType::SegmentColResult         SegmentColResult;

		typedef HierarchyMesh <mesh_traits>                         MeshType ;

	public: 
	    HierarchyMesh(): mp_tree( NULL ) {}
		virtual ~HierarchyMesh() 
		{
			if (this->mp_tree != NULL)
				delete this->mp_tree; 
		}
		AABBTreePolyType * build_hierarchy ()
		{
			if (this->mp_tree != NULL) delete this->mp_tree; 
			AABBTreePolyBuilderType builder ;
			this->mp_tree = builder.build_from_mesh ( (Mesh<mesh_traits > &) (* this), 1);
			return this->mp_tree; 
		}
		AABBTreePolyType * get_aabbtree() const 
		{
			return this->mp_tree; 
		}

		bool is_intersect (const PolygonType & polygon) const 
		{
			PolygonColResult result ;
			this->mp_tree->collideWithPolygon (polygon, result);
			return result.polys_.size() > 0; 
		}

		bool is_intersect (const SphereType & sphere) const 
		{
			SphereColResult result ;
			this->mp_tree->collideWithSphere (sphere, result);
			return result.polys_.size() > 0; 
		}
/*
		//v_face_id return the intersected face id. 
		bool intersect (const MeshType & obj, std::vector<unsigned> & v_face_id) const
		{
			for (unsigned i = 0; i < obj.faces.size(); ++i)
			{
				PolygonType pol; 
				pol = face2polygon (obj.faces[i]) ;
				if (obj.is_intersect (pol) ) return true; 
			}
			return false ;
		}
*/
		bool intersect (const PolygonType & polygon, std::vector<unsigned > & v_face_id) const 
		{
			PolygonColResult result ;			
			this->mp_tree->collideWithPolygon (polygon, result);
			if ( result.polys_.size() == 0) return false; 
			for (unsigned i = 0; i < result.polys_.size(); ++i)
			{
				v_face_id.push_back(result.polys_[i]->get_face()->m_idx);
			} 
			return true; 
		}


		bool intersect (const SphereType & sphere, std::vector<unsigned > & v_face_id)
		{
			SphereColResult result ;			
			this->mp_tree->collideWithSphere (sphere, result);
			if ( result.polys_.size() == 0) return false; 
			for (unsigned i = 0; i < result.polys_.size(); ++i)
			{
				v_face_id.push_back(result.polys_[i]->get_face()->m_idx);
			} 
			return true; 
		}

		bool intersect (const SegmentType & seg, std::vector<unsigned> & v_face_id )
		{
			SegmentColResult result ;
			this->mp_tree->collideWithSegment (seg, result);
			if ( result.polys_.size() == 0) return false; 
			for (unsigned i = 0; i < result.polys_.size(); ++i)
			{
				v_face_id.push_back(result.polys_[i]->get_face()->m_idx);
			} 
			return true; 
		}

		bool intersect (const RayType & ray, std::vector<unsigned> & v_face_id )
		{
			RayColResult result ;
			this->mp_tree->collideWithSegment (ray, result);
			if ( result.polys_.size() == 0) return false; 
			for (unsigned i = 0; i < result.polys_.size(); ++i)
			{
				v_face_id.push_back(result.polys_[i]->get_face()->m_idx);
			} 
			return true; 
		}

		bool segment_pick (const SegmentType & seg, std::vector<unsigned> & v_face_id )
		{
			SegmentColResult result ;
			this->mp_tree->SegmentPick (seg, result);
			if ( result.polys_.size() == 0) return false; 
			for (unsigned i = 0; i < result.polys_.size(); ++i)
			{
				v_face_id.push_back(result.polys_[i]->get_face()->m_idx);
			} 
			return true; 
		}

	protected :
		PolygonType face2polygon (const FaceType * face) const 
		{
			PolygonType pol ;
			std::vector<PointType *> v_set ;
			face->Face2VertexCoord (v_set);
			for (unsigned i = 0; i  < v_set.size(); ++i)
				pol.append_vertex ( * v_set[i]);
			return pol ;
		}
	private :
		AABBTreePolyType * mp_tree; 
	};
};
#endif
